package com.binarycat.dynamiclimit.limiter.policy;

import java.util.concurrent.atomic.AtomicBoolean;

import com.binarycat.dynamiclimit.annotation.DynamicLimit;
import com.binarycat.dynamiclimit.bean.DynamicLimiterConfig;
import com.binarycat.dynamiclimit.bean.LimitPolicyEnum;
import com.binarycat.dynamiclimit.constant.CommonLimitConstants;
import com.binarycat.dynamiclimit.metric.SlidingHistory;
import com.binarycat.dynamiclimit.util.CommonLimitUtils;
import com.binarycat.dynamiclimit.util.LimitRateLogUtils;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.google.common.util.concurrent.RateLimiter;

/**
 * 依据超时率的限流策略
 * 
 * @author jjb
 * @date 2018/08/06
 */
public class TimeoutRateLimitPolicy extends AbstractMethodLevelLimitPolicy {

    @Override
    protected boolean startLimit() {
        filterLimitTargetMethod();

        boolean limitStatus = limitTargetMethodMappingLimitData.getLimitRateStatus().get();
        DynamicLimiterConfig config = dynamicLimiterManager.getDynamicLimiterConfig();
        MetricRegistry metricRegistry = dynamicLimiterManager.getLimitData().getMetricRegistry();
        String methodName = limitTargetMethodMappingLimitData.getMethodName();
        SlidingHistory slidingHistory = (SlidingHistory)metricRegistry.getMetrics()
            .get(MetricRegistry.name(methodName, CommonLimitConstants.METRIC_TIMEOUT));

        // 该方法的平均响应时间
        double meanTime = slidingHistory.getDataListMean();
        metricConditionValueTem = meanTime;

        // 该方法的最近一分钟内的qps
        double qpsInOneMinute = ((Meter)limitTargetMetric).getOneMinuteRate();
        metricQpsValueTem = qpsInOneMinute;
        if (meanTime > config.getTimeOutLimitRateThreshold() && qpsInOneMinute > config.getQpsProtectionValue()) {
            // 开启限流
            if (!limitStatus) {
                limitTargetMethodMappingLimitData.getRateLimiter().setRate(qpsInOneMinute);
                limitTargetMethodMappingLimitData.getLimitRateStatus().set(true);
                LimitRateLogUtils.log("超时率的限流策略开始限流");
            }
            return true;

        }
        // 该策略从来没有进行过限流
        if (limitTargetMethodMappingLimitData == null) {
            return false;
        }

        return limitTargetMethodMappingLimitData.getLimitRateStatus().get();
    }

    @Override
    protected boolean reduceLimitThreshold() {

        DynamicLimiterConfig dynamicLimiterConfig = dynamicLimiterManager.getDynamicLimiterConfig();

        // 未开始限流
        if (!limitTargetMethodMappingLimitData.getLimitRateStatus().get()) {
            return false;
        }

        // 超时率未超过阀值
        if (metricConditionValueTem <= dynamicLimiterConfig.getTimeOutLimitRateThreshold()) {
            return false;
        }
        RateLimiter rateLimiter = limitTargetMethodMappingLimitData.getRateLimiter();
        double qpsChangeRate = dynamicLimiterManager.getDynamicLimiterConfig().getQpsChangeRate();
        double rate = rateLimiter.getRate();
        double limitRateReduceValue = rate * qpsChangeRate;
        int qpsProtectionValue = dynamicLimiterConfig.getQpsProtectionValue();
        double qps = rate - limitRateReduceValue;
        if (qps < qpsProtectionValue) {
            qps = qpsProtectionValue;
        }
        LimitRateLogUtils.log("超时率的限流策略开始降低限流阀值,降低了" + Math.round(rate - qps));
        rateLimiter.setRate(Math.round(qps));
        return true;
    }

    @Override
    protected boolean endLimit() {
        double qpsInOneMinute = metricQpsValueTem;
        double meanTime = metricConditionValueTem;
        DynamicLimiterConfig config = dynamicLimiterManager.getDynamicLimiterConfig();
        RateLimiter rateLimiter = limitTargetMethodMappingLimitData.getRateLimiter();
        AtomicBoolean limitRateStatus = limitTargetMethodMappingLimitData.getLimitRateStatus();
        if (!limitRateStatus.get()) {
            return true;
        }
        // 平均响应时间低于低于阀值的一半,或者当前限流器的速率高于实际的度量的速率的两倍则关闭限流
        if (meanTime <= (config.getTimeOutLimitRateThreshold() * RATIO)
            || rateLimiter.getRate() >= (qpsInOneMinute * 2)) {
            limitRateStatus.set(false);
            LimitRateLogUtils.log("超时率的限流策略关闭限流");
            return true;
        }
        return false;
    }

    @Override
    protected boolean increaseLimitThreshold() {
        DynamicLimiterConfig dynamicLimiterConfig = dynamicLimiterManager.getDynamicLimiterConfig();
        // 未开始限流
        if (!limitTargetMethodMappingLimitData.getLimitRateStatus().get()) {
            return false;
        }
        // 超时率超过阀值
        if (metricConditionValueTem >= dynamicLimiterConfig.getTimeOutLimitRateThreshold()) {
            return false;
        }

        RateLimiter rateLimiter = limitTargetMethodMappingLimitData.getRateLimiter();
        double qpsChangeRate = dynamicLimiterManager.getDynamicLimiterConfig().getQpsChangeRate();
        double limitRateReduceValue = rateLimiter.getRate() * qpsChangeRate;
        rateLimiter.setRate(Math.round(rateLimiter.getRate() + limitRateReduceValue));
        LimitRateLogUtils.log("超时率限流策略开始升高限流阀值,升高了" + Math.round(limitRateReduceValue));
        return true;
    }

    @Override
    public boolean supportLimitPolicy() {
        DynamicLimit dynamicLimit = CommonLimitUtils.getDynamicLimitAnnotation(handlerMethod);
        if(dynamicLimit==null) {
            return false;
        }
        boolean isExecuteResultLimit = dynamicLimit.limitPolicy() == LimitPolicyEnum.EXECUTE_RESULT_LIMIT;
        boolean isTimeoutLimit = dynamicLimit.limitPolicy() == LimitPolicyEnum.TIMEOUT_LIMIT;
        if (isExecuteResultLimit || isTimeoutLimit) {
            return true;
        }
        return false;
    }

}
